/************************************************************************************ 
 * NOVA.cs
 * 
 * Author      : Hristo Hristov
 * Datum       : 06.04.2011
 * Version     : 1.0
 * Revision    : 5
 * Description : This class is the main class of the NOVA framework. 
 * 
 *************************************************************************************/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using NOVA.Components;
using NOVA.Components.Move;

using NOVA.Physics;
using BEPUphysicsDrawer.Models;
using BEPUphysics.Entities;
using BEPUphysics;
using NOVA.Utilities;
using BEPUphysics.CollisionShapes.ConvexShapes;
using NOVA.ScreenManagement;

namespace NOVA
{
    /// <summary>
    /// This class is the main class of the NOVA framework. 
    /// </summary>
    public class NOVAEngine : Microsoft.Xna.Framework.DrawableGameComponent
    {
        #region Members

        // Static engine setting
        private static EngineSettings settings = new EngineSettings();
        
        Color m_colorBackground;
        GraphicsDeviceManager m_graphics;
        ContentManager m_Content;
        ScreenManager m_screenManager;
        string[] m_strPreloadAssets;
        public PSMoveCalibration cal;

        #region Move Members
        //public MarkerNode moveMarkerNode;
        #endregion

        private MouseState prevMouseState = new MouseState();
        private Vector2 angles = Vector2.Zero;
        int widthOver2;
        int heightOver2;


        #endregion

        #region Properties
        
        public GraphicsDeviceManager GraphicsDeviceManager
        {
            get { return m_graphics; }
            set { m_graphics = value; }
        }

        public ContentManager ContentManager
        {
            get { return m_Content; }
            set { m_Content = value; }
        }

        public Color BackgroundColor
        {
            get { return m_colorBackground; }
            set { m_colorBackground = value; }
        }

        public static EngineSettings Settings
        {
            get { return settings; }
        }

        public ScreenManager ScreenManager { get { return m_screenManager; } }

        public string[] PreloadAssets 
        {
            get { return m_strPreloadAssets; }
            set { m_strPreloadAssets = value; }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// This class is the main class of the NOVA framework. 
        /// </summary>
        public NOVAEngine(Game game, GraphicsDeviceManager graphics, ContentManager content) : base(game)
        {
            // Save local variables
            this.m_graphics = graphics;
            this.m_Content = content;

            // Add this component itself to the game components for automatic update and drawing
            game.Components.Add(this);

            // Create the screen manager component.
            m_screenManager = new ScreenManager(game, m_graphics);
            //game.Components.Add(m_screenManager);
            //m_screenManager.DrawOrder = 0;

            // Initialize GoblinXNA
            //State.Init(m_graphics, m_Content); 
            Core.Init(game, m_graphics, m_Content, m_screenManager);

            NOVA.Utilities.DebugShapeRenderer.Initialize();

            widthOver2 = Game.Window.ClientBounds.Width / 2;
            heightOver2 = Game.Window.ClientBounds.Height / 2;

            
            

            //cal = new PSMoveCalibration(game);
            //game.Components.Add(cal);            
        }
        #endregion
        
        #region Overrides
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {/*
#if DEBUG
            //ModelDrawer = new BruteModelDrawer(this.Game); //InstancedModelDrawer(this.Game);
#endif

            //m_scene.BackgroundColor = m_colorBackground;

            //State.ThreadOption = (ushort)ThreadOptions.MarkerTracking;

            //m_scene.MarkerTracker = PSMove.Instance.Tracker;
            //moveMarkerNode = new MarkerNode(PSMove.Instance.Tracker, "Marker");
            //m_scene.RootNode.AddChild(moveMarkerNode);

            //SetupMarkerTracking();
            
            // Set up the JigLibX physics engine
            //CreatePhysics();

            // Set up the lights used in the scene
            //CreateLights();

            // Set up the camera which defines the eye location and viewing frustum
            //CreateCamera();

            // Create 3D objects
            //CreateObjects();
            
            //m_scene.PreferPerPixelLighting = true;
            

            // Add a mouse click handler for shooting a box model from the mouse location 
            //MouseInput.Instance.MouseClickEvent += new HandleMouseClick(MouseClickHandler);
            
            // Add the JigLibX physics debugDrawer to the game components
            //Game.Components.Add(debugDrawer);
            //debugDrawer.DrawOrder = 1;

            // Show some debug information
            State.ShowFPS = true;
            State.ShowTriangleCount = true;
            State.ShowNotifications = true;

            //m_scene.RenderCollisionMesh = true;
            //m_scene.CollisionMeshColor = Color.White;

            settings.Save("settings.xml");
            //settings = EngineSettings.Load("settings.xml") as EngineSettings;
            base.Initialize();
            
            int width = 10;
            int height = 10;
            float blockWidth = 2f;
            float blockHeight = 1f;
            float blockLength = 1f;

            Material groundMat = new Material();
            groundMat.Diffuse = Color.Blue.ToVector4();
            groundMat.Specular = Color.Yellow.ToVector4();
            groundMat.SpecularPower = 10;

            Material boxMat = new Material();
            boxMat.Diffuse = Color.Green.ToVector4();
            boxMat.Specular = Color.Yellow.ToVector4();
            boxMat.SpecularPower = 10;

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
#if MODEL_DRAWER
                    var toAdd =
                        new BEPUphysics.Entities.Prefabs.Box(
                            new Vector3(
                                i * blockWidth + .5f * blockWidth * (j % 2) - width * blockWidth * .5f,
                                blockHeight * .5f + j * (blockHeight),
                                -5),
                            blockWidth, blockHeight, blockLength, 10);
                    ((BEPUPhysics)m_scene.PhysicsEngine).Space.Add(toAdd);
                    ModelDrawer.Add(toAdd);
#else
                    GeometryNode box = new GeometryNode();
                    TransformNode trans = new TransformNode();
                    trans.Translation = new Vector3(
                                i * blockWidth + .5f * blockWidth * (j % 2) - width * blockWidth * .5f,
                                blockHeight * .5f + j * (blockHeight),
                                -5);
                    box.Model = new Box(blockWidth, blockHeight, blockLength);
                    box.Material = boxMat;
                    box.Physics.Interactable = true;
                    box.Physics.Mass = 10.0f; 
                    box.AddToPhysicsEngine = true;
                    //m_scene.RootNode.AddChild(trans);
                    trans.AddChild(box);
#endif
                }
            }
#if MODEL_DRAWER
            BEPUphysics.Entities.Prefabs.Box ground = new BEPUphysics.Entities.Prefabs.Box(new Vector3(0, -.5f, 0), 50, 1, 50);
            ((BEPUPhysics)m_scene.PhysicsEngine).Space.Add(ground);
            ModelDrawer.Add(ground);
            Cam.Position = new Vector3(0, 6, 15);
#else

            GeometryNode ground = new GeometryNode();
            ground.Model = new Box(50, 1, 50);
            ground.Material = groundMat;
            ground.Physics.ApplyGravity = false;
            ground.Physics.Interactable = true;
            ground.Physics.Mass = 0.0f; 
            ground.AddToPhysicsEngine = true;
            //m_scene.RootNode.AddChild(ground);
#endif*/
            m_screenManager.Initialize();
            base.Initialize();
        }

        /// <summary>
        /// This is called when the component should load it's content.
        /// </summary>
        protected override void LoadContent()
        {
            // Load all assets for the screenmanager
            //foreach (string asset in m_strPreloadAssets)
            //    m_Content.Load<object>(asset);

            m_screenManager.LoadContent();

            base.LoadContent();                    
        }

        /// <summary>
        /// This is called when the game component should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            //GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.CornflowerBlue, 1f, 0);
            
            //base.Draw(gameTime);

            m_screenManager.Draw(gameTime);
            
            /*
            debugDrawer.Enabled = true;
            debugDrawer.View = State.ViewMatrix;
            debugDrawer.Projection = State.ProjectionMatrix;

            RecursiveRendering(m_scene.RootNode);
            debugDrawer.Draw(gameTime);  */

//#if DEBUG        
            //ModelDrawer.Draw(Cam.ViewMatrix, Cam.ProjectionMatrix);
            //State.Device.Reset();
            //State.Device.DepthStencilState = DepthStencilState.None;
            //State.Device.SamplerStates[0] = SamplerState.LinearWrap;
            //ModelDrawer.Draw(camera.View, camera.Projection);//State.ViewMatrix, State.ProjectionMatrix);
//#endif
            base.Draw(gameTime);
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Update the input data from the PS Move controller
            //PSMove.Instance.Update();

            double elapsedTime = (double)gameTime.ElapsedGameTime.Ticks / (double)TimeSpan.TicksPerSecond;
            //ProcessInput((float)elapsedTime * 10.0f);
            //UpdateCameraView();

            m_screenManager.Update(gameTime);

            base.Update(gameTime);
#if DEBUG
            //space.Update();

            //Keep the mouse within the screen
            //Mouse.SetPosition(200, 200);
            //Cam.Update((float)gameTime.ElapsedGameTime.TotalSeconds, KeyboardInput, MouseInput, GamePad.GetState(0));
            //ModelDrawer.Update();
#endif
           
        }

        /// <summary>
        /// Dispose the game components
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Dispose(bool disposing)
        {
            //PSMove.Instance.Dispose();

            m_screenManager.Dispose();

            base.Dispose(disposing);
        }

        #endregion

        #region Public Methods
        ///// <summary>
        ///// Recursivly works off the given node and it's children and it's collision
        ///// mesh using the JigLibX debug drawer.
        ///// </summary>
        ///// <param name="WindowHandle">A handle to a window, which has to be set for the Move controller.</param>
        ///// <param name="bDoTracking">Initialize the PS Move controller also for optical tracking based on 
        ///// the "MarkerTracker" of the current scene.</param>
        //public void InitializeMove(IntPtr WindowHandle, bool bDoTracking)
        //{
        //    // This is important!! Set the window handle for Move HID
        //    PSMove.Instance.moveWindowHandle = WindowHandle;

        //    // Initialize the Move framework
        //    PSMove.Instance.Initialize();

        //    // Initialize the optical tracking by creating a Move marker node
        //    if (bDoTracking)
        //    {
        //        PSMove.Instance.Tracker.InitTracker(new Color(0, 0, 255));
                
        //    }    
        //}
        #endregion
    }
}
